use std::fmt::Display;

use crate::gen_title;

pub fn ch2_23_1() {
    gen_title("ch2_23_1", 18);

    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);

    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() {
            x
        } else {
            y
        }
    }
}

pub fn ch2_23_2() {
    gen_title("ch2_23_2", 18);

    struct ImportantExcerpt<'a> {
        part: &'a str,
    }

    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split(".").next().expect("Could not find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
    println!("{}", i.part);
}

pub fn ch2_23_3() {
    gen_title("ch2_23_3", 18);

    #[derive(Debug)]
    struct ImportantExcerpt<'a> {
        part: &'a i32,
    }

    let i;
    {
        i = ImportantExcerpt { part: &1 };
    }
    println!("{}", i.part);
}

pub fn ch2_23_4() {
    gen_title("ch2_23_4", 18);

    struct ImportantExcerpt<'a> {
        part: &'a str,
    }

    impl<'a> ImportantExcerpt<'a> {
        fn level(&self) -> i32 {
            3
        }
    }

    println!("{}", ImportantExcerpt { part: "Hello" }.level());

    impl<'a> ImportantExcerpt<'a> {
        fn announce_and_return_part(&self, announcement: &str) -> &str {
            println!("Attention please: {}", announcement);
            self.part
        }
    }

    println!(
        "{}",
        ImportantExcerpt { part: "Hello" }.announce_and_return_part("Hello")
    );

    impl<'a: 'b, 'b> ImportantExcerpt<'a> {
        fn announce_and_return_part_b(&'a self, announcement: &'b str) -> &'b str {
            println!("Attention please: {}", announcement);
            self.part
        }
    }

    println!(
        "{}",
        ImportantExcerpt { part: "Hello" }.announce_and_return_part_b("Hello")
    );
}

pub fn ch2_23_5() {
    gen_title("ch2_23_5", 18);

    fn longest_with_an_announcement<'a, T>(x: &'a str, y: &'a str, ann: T) -> &'a str
    where
        T: Display,
    {
        println!("Announcement! {}", ann);

        if x.len() > y.len() {
            x
        } else {
            y
        }
    }

    println!(
        "{}",
        longest_with_an_announcement("Hello", "World", "Hello World")
    );
}

pub fn ch2_23_pra_1() {
    gen_title("ch2_23_pra_1", 18);

    let i = 3;
    {
        let borrow1 = &i;
        println!("{}", borrow1);
    }
    {
        let borrow2 = &i;
        println!("{}", borrow2);
    }
}

pub fn ch2_23_pra_2() {
    gen_title("ch2_23_pra_2", 18);

    let x = 5;
    let r = &x;

    println!("{}", r);
}

pub fn ch2_23_pra_3() {
    gen_title("ch2_23_pra_3", 18);

    fn print_one<'a>(x: &'a i32) {
        println!("`print_one`: x is {}", x);
    }

    fn add_one<'a>(x: &'a mut i32) {
        *x += 1
    }

    fn print_multi<'a, 'b>(x: &'a i32, y: &'b i32) {
        println!("`print_multi`: x is {}, y is {}", x, y);
    }

    fn pass_x<'a, 'b>(x: &'a i32, _: &'b i32) -> &'a i32 {
        x
    }

    let x = 7;
    let y = 9;

    print_one(&x);
    print_multi(&x, &y);

    let z = pass_x(&x, &y);
    print_one(z);

    let mut t = 3;
    add_one(&mut t);
    print_one(&t);
}

pub fn ch2_23_pra_4() {
    gen_title("ch2_23_pra_4", 18);

    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() {
            x
        } else {
            y
        }
    }

    println!("{}", longest("Hello", "World"));
}

pub fn ch2_23_pra_5() {
    gen_title("ch2_23_pra_5", 18);

    fn invalid_output1() -> String {
        String::from("foo")
    }

    println!("{}", invalid_output1());

    fn invalid_output2<'a>(x: &'a String) -> &'a String {
        x
    }
    println!("{}", invalid_output2(&String::from("foo")));

    fn invalid_output3() -> &'static str {
        "foo"
    }
    println!("{}", invalid_output3());
}

pub fn ch2_23_pra_6() {
    gen_title("ch2_23_pra_6", 18);

    fn print_refs<'a, 'b>(x: &'a i32, y: &'b i32) {
        println!("`print_refs`: |x: {}, y: {}", x, y);
    }

    fn failed_borrow() {
        let _x = 12;
        let y: &i32 = &_x;
        println!("{}", y);
    }

    let (four, nine) = (4, 9);

    print_refs(&four, &nine);

    failed_borrow();
}

pub fn ch2_23_pra_7() {
    gen_title("ch2_23_pra_7", 18);

    #[derive(Debug)]
    struct Borrowed<'a>(&'a i32);

    #[derive(Debug)]
    struct NamedBorrowed<'a> {
        x: &'a i32,
        y: &'a i32,
    }

    #[derive(Debug)]
    enum Either<'a> {
        Num(i32),
        Ref(&'a i32),
    }

    let x = 18;
    let y = 15;

    let single = Borrowed(&x);
    let double = NamedBorrowed { x: &x, y: &y };

    let reference = Either::Ref(&x);
    let number = Either::Num(y);

    println!("x is borrowed in {:?}", single);
    println!("x and y are borrowed in {:?}", double);
    println!("x is borrowed in {:?}", reference);
    println!("y is *not* borrowed in {:?}", number);
}

pub fn ch2_23_pra_8() {
    gen_title("ch2_23_pra_8", 18);

    #[derive(Debug)]
    struct NoCopyType {}

    #[derive(Debug)]
    struct Example<'a, 'b> {
        a: &'a u32,
        b: &'b NoCopyType,
    }

    let var_a = 35;
    let example: Example;
    let var_b = NoCopyType {};
    example = Example {
        a: &var_a,
        b: &var_b,
    };

    println!("{:?}", example);
}

pub fn ch2_23_pra_9() {
    gen_title("ch2_23_pra_9", 18);
    #[derive(Debug)]
    struct NoCopyType {}

    #[derive(Debug)]
    #[allow(dead_code)]
    struct Example<'a, 'b> {
        a: &'a u32,
        b: &'b NoCopyType,
    }

    fn fix_me<'a>(foo: &'a Example) -> &'a NoCopyType {
        foo.b
    }

    let no_copy = NoCopyType {};
    let example = Example { a: &1, b: &no_copy };
    fix_me(&example);
    println!("{:?}", example);
    println!("{:?}", fix_me(&example));
}

pub fn ch2_23_pra_10() {
    gen_title("ch2_23_pra_10", 18);

    struct Owner(i32);

    impl Owner {
        fn add_one<'a>(&'a mut self) {
            self.0 += 1;
        }
        fn print<'a>(&'a self) {
            println!("{}", self.0);
        }
    }

    let mut owner = Owner(18);

    owner.add_one();
    owner.print();
}

pub fn ch2_23_pra_11() {
    gen_title("ch2_23_pra_11", 18);

    struct ImportantExcerpt<'a> {
        part: &'a str,
    }

    impl<'a> ImportantExcerpt<'a> {
        fn level(&'a self) -> i32 {
            3
        }
    }

    let imp = ImportantExcerpt { part: "string" };
    println!("{}", imp.level());
}

pub fn ch2_23_pra_12() {
    gen_title("ch2_23_pra_12", 18);

    fn nput(x: &i32) {
        println!("{}", x);
    }

    fn pass(x: &i32) -> &i32 {
        println!("{}", x);
        x
    }

    fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str {
        x
    }

    struct Owner(i32);

    impl Owner {
        fn add_one(&mut self) {
            self.0 += 1;
        }

        fn print(&self) {
            println!("{}", self.0);
        }
    }

    struct Person<'a> {
        age: u8,
        name: &'a str,
    }

    enum Either<'a> {
        Num(i32),
        Ref(&'a i32),
    }

    println!("{}", longest("Hello", "World"));
}
